АНАЛИЗ ТОВАРНОГО АССОРТИМЕНТА

**Описание проекта:**

Интернет-магазин товаров для дома «Пока все ещё тут» в срочном порядке ищет аналитиков. Надо помочь магазину стать лучше, а клиентам — обустроить дом своей мечты. «Пока все ещё тут» — мы создаём уют!

**Заказчик отчета:** Менеджер по продукту/категорийный менеджер

**Цель:** выяснить «ненужный» товарный ряд для выведения их из продуктовой линейки с помощью выявления основого и дополнительного товаров. Оптимизация товарного ассортимента.

**Задачи:**

  • Распределить товар на основной и дополнительный
  • Проведение ииследовательского анализа данных
  • Проверка статистических гипотез

**Описание данных**

Колонки в ecommerce_dataset.csv :

  • date — дата заказа;
  • customer_id — идентификатор покупателя;
  • order_id — идентификатор заказа;
  • product — наименование товара;
  • quantity — количество товара в заказе;
  • price — цена товара.

**Оглавление**

  • 1. Изучение входных данных

  • 2. Предобработка данных

    • 2.1. Исходные данные таблицы/создание столбцов
    • 2.2. Проверим дубликаты и пропуски
    • 2.3. Проверка типов данных
    • 2.4. Проверим выбросы
    • 2.5. Проверка на уникальность заказов/клиентов
  • 3. Анализ данных (EDA)

    • 3.1. Количество товаров в разрезе заказов
    • 3.2. Количество уникальных покупателей в разрезе месяца, дня недели и времени
    • 3.3. Количество заказов в разрезе месяца, дня недели и времени
    • 3.4. Лидеры продаж топ-15 товаров
    • 3.5. Аутсайдеры продаж топ-15 товаров
  • 4. Детализация исследования: анализ товарного ассортимента

    • 4.1. Сгруппируем данные по категориям и найдем топ-10
    • 4.2. Средняя выручка по категориям и месяцам
    • 4.3. Соотношение основых и дополнительных товаров
    • 4.4. Определим сезонность по категориям
  • 5. Проверка гипотез

    • 5.1. Проверка гипотезы №1
    • 5.2. Проверка гипотезы №1
  • 6. Общий вывод и рекомендации

  • 7. Презентация для заказчика

  • 8. Дашборт

**Выполнение проекта**

Изучение входных данных¶

In [1]:
# Загружаем библиотеки
import pandas as pd
import numpy as np
import datetime as dt
from matplotlib.pyplot import figure
import warnings
warnings.filterwarnings('ignore')
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.pylab as pylab
import seaborn as sns
from matplotlib.ticker import FuncFormatter
from plotly import graph_objects as go
import plotly.express as px
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import folium
In [2]:
# Путь к внешней таблице
url = 'https://drive.google.com/file/d/1Q89QFO8eNumV6PwHeySnJ7g7kFA4PL0H/view?usp=sharing'
url='https://drive.google.com/uc?id=' + url.split('/')[-2]
df = pd.read_csv(url)

df.head()
Out[2]:
date customer_id order_id product quantity price
0 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Алое Вера, d12, h30 1 142.0
1 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Кофе Арабика, d12,... 1 194.0
2 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Радермахера d-12 см h-20 см 1 112.0
3 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Хризолидокарпус Лутесценс d-9 см 1 179.0
4 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Циперус Зумула d-12 см h-25 см 1 112.0
In [3]:
# Выведим основную информацию о датафрейме с помощью метода `info()` 
# Изучим столбцы и их типы
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6737 entries, 0 to 6736
Data columns (total 6 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   date         6737 non-null   int64  
 1   customer_id  6737 non-null   object 
 2   order_id     6737 non-null   int64  
 3   product      6737 non-null   object 
 4   quantity     6737 non-null   int64  
 5   price        6737 non-null   float64
dtypes: float64(1), int64(3), object(2)
memory usage: 315.9+ KB
In [4]:
# Узнаем количество строк и столбцов в датафрейме
df.shape
Out[4]:
(6737, 6)
In [5]:
# Просмотр сводной статистики
df.describe().T
Out[5]:
count mean std min 25% 50% 75% max
date 6737.0 2.018855e+09 385518.465620 2.018100e+09 2.019020e+09 2.019050e+09 2.019061e+09 2.019103e+09
order_id 6737.0 4.312895e+04 27899.414662 1.262400e+04 1.482700e+04 6.850300e+04 7.050400e+04 7.316400e+04
quantity 6737.0 2.501559e+00 15.266478 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+03
price 6737.0 4.620285e+02 871.296064 9.000000e+00 1.010000e+02 1.350000e+02 3.980000e+02 1.491700e+04

Предобработка данных¶

Исходные данные таблицы/создание столбцов¶

Перед обработкой данных выведим на экран исходные данные, чтобы понимать дальнейшие изменения.

In [6]:
# Количество строк
print('Количество строк:', len(df))
# Количество уникальных покупателей
print('Количество уникальных покупателей:', len(df['customer_id'].unique()))
# Количество уникальных заказов
print('Количество уникальных заказов:', len(df['order_id'].unique()))
# Количество проданного товара
print('Количество проданного товара:', (df['quantity'].sum()))
# Общая выручка 
df['revenue']= (df['price']* df['quantity']) #  добавим новый столбец с выручкой
print('Общая выручка:',df['revenue'].sum())
# Средний чек заказа
order_sum = df.groupby('order_id').agg({'revenue': 'sum'}).reset_index()
print('Средний чек заказа:', order_sum['revenue'].mean())
Количество строк: 6737
Количество уникальных покупателей: 2451
Количество уникальных заказов: 2784
Количество проданного товара: 16853
Общая выручка: 4851280.0
Средний чек заказа: 1742.557471264368

Проверим дубликаты и пропуски¶

In [7]:
# Проверим наличие пропусков в каждом столбце(%)
pd.DataFrame(round(df.isna().mean()*100,)).style.background_gradient('BuPu') 
Out[7]:
  0
date 0.000000
customer_id 0.000000
order_id 0.000000
product 0.000000
quantity 0.000000
price 0.000000
revenue 0.000000
In [8]:
# Проверим наличие дубликатов
df.duplicated().sum()
Out[8]:
0
In [9]:
# Удалим неявные дубликаты при совпадении трех столбцов
df_up= df.drop_duplicates(subset=['customer_id', 'order_id', 'product'], keep='last')
In [10]:
# Узнаем количество строк и столбцов в датафрейме
df_up.shape
Out[10]:
(4851, 7)
In [11]:
print('Всего удаленно строк:', len(df)-len(df_up))
Всего удаленно строк: 1886

Проверка типов данных¶

In [12]:
# Изучим столбцы и их типы
df_up.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 4851 entries, 0 to 6736
Data columns (total 7 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   date         4851 non-null   int64  
 1   customer_id  4851 non-null   object 
 2   order_id     4851 non-null   int64  
 3   product      4851 non-null   object 
 4   quantity     4851 non-null   int64  
 5   price        4851 non-null   float64
 6   revenue      4851 non-null   float64
dtypes: float64(2), int64(3), object(2)
memory usage: 303.2+ KB

Заменим тип данных в столбце date на datetime64

In [13]:
df_up['date'] = pd.to_datetime(df['date'], format = '%Y%m%d%H')

Добавим к таблице новые столбцы:

  • year- год;
  • quartel- квартал;
  • month- месяц;
  • week- день недели;
  • day- день;
  • hour- час
In [14]:
df_up['year'] = df_up['date'].astype('datetime64[Y]')
df_up['quartel'] = df_up['date'].dt.quarter
df_up['month'] = df_up['date'].astype('datetime64[M]')
df_up['week'] = df_up['date'].dt.day_name()
df_up['day'] = df_up['date'].astype('datetime64[D]')
df_up['hour'] = df_up['date'].dt.hour
In [15]:
# Проверим столбцы и их типы
df_up.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 4851 entries, 0 to 6736
Data columns (total 13 columns):
 #   Column       Non-Null Count  Dtype         
---  ------       --------------  -----         
 0   date         4851 non-null   datetime64[ns]
 1   customer_id  4851 non-null   object        
 2   order_id     4851 non-null   int64         
 3   product      4851 non-null   object        
 4   quantity     4851 non-null   int64         
 5   price        4851 non-null   float64       
 6   revenue      4851 non-null   float64       
 7   year         4851 non-null   datetime64[ns]
 8   quartel      4851 non-null   int64         
 9   month        4851 non-null   datetime64[ns]
 10  week         4851 non-null   object        
 11  day          4851 non-null   datetime64[ns]
 12  hour         4851 non-null   int64         
dtypes: datetime64[ns](4), float64(2), int64(4), object(3)
memory usage: 530.6+ KB
In [16]:
# Проверим столбцы
df_up.head()
Out[16]:
date customer_id order_id product quantity price revenue year quartel month week day hour
0 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Алое Вера, d12, h30 1 142.0 142.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0
1 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Кофе Арабика, d12,... 1 194.0 194.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0
2 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Радермахера d-12 см h-20 см 1 112.0 112.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0
3 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Хризолидокарпус Лутесценс d-9 см 1 179.0 179.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0
4 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Циперус Зумула d-12 см h-25 см 1 112.0 112.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0

Проверим выбросы¶

Проверка столбца quantity

In [17]:
df_up['quantity'].describe(percentiles=[0.1, 0.5, 0.6
, 0.7, 0.8, 0.88, 0.9, 0.95]).T
Out[17]:
count    4851.000000
mean        2.835910
std        17.642155
min         1.000000
10%         1.000000
50%         1.000000
60%         1.000000
70%         1.000000
80%         2.000000
88%         3.000000
90%         3.000000
95%         8.000000
max      1000.000000
Name: quantity, dtype: float64
In [18]:
#Найдем заказ с количеством 1000
df_up.sort_values(by='quantity', ascending=False)[:5]
Out[18]:
date customer_id order_id product quantity price revenue year quartel month week day hour
5456 2019-06-18 15:00:00 312e9a3e-5fca-43ff-a6a1-892d2b2d5ba6 71743 Вантуз с деревянной ручкой d14 см красный, Bur... 1000 675.0 675000.0 2019-01-01 2 2019-06-01 Tuesday 2019-06-18 15
5071 2019-06-11 07:00:00 146cd9bf-a95c-4afb-915b-5f6684b17444 71668 Вешалки мягкие для деликатных вещей 3 шт шоколад 334 148.0 49432.0 2019-01-01 2 2019-06-01 Tuesday 2019-06-11 7
3961 2019-05-20 21:00:00 5d189e88-d4d6-4eac-ab43-fa65a3c4d106 71478 Муляж ЯБЛОКО 9 см красное 300 51.0 15300.0 2019-01-01 2 2019-05-01 Monday 2019-05-20 21
1158 2018-12-10 14:00:00 a984c5b7-ff7e-4647-b84e-ef0b85a2762d 69289 Ручка-скоба РС-100 белая *Трибатрон*, 1108035 200 29.0 5800.0 2018-01-01 4 2018-12-01 Monday 2018-12-10 14
568 2018-11-01 08:00:00 aa42dc38-780f-4b50-9a65-83b6fa64e766 68815 Муляж ЯБЛОКО 9 см красное 170 51.0 8670.0 2018-01-01 4 2018-11-01 Thursday 2018-11-01 8
In [19]:
#Удаляем выбросы, так как это были оптовые покупки
df_up = df_up[(df_up['quantity'] < 1000)]
In [20]:
# Проверим
df_up['quantity'].describe(percentiles=[0.1, 0.5, 0.6
, 0.7, 0.8, 0.88, 0.9, 0.95]).T
Out[20]:
count    4850.000000
mean        2.630309
std        10.305702
min         1.000000
10%         1.000000
50%         1.000000
60%         1.000000
70%         1.000000
80%         2.000000
88%         3.000000
90%         3.000000
95%         8.000000
max       334.000000
Name: quantity, dtype: float64
In [21]:
# Построим диаграмму размаха("ящик с усами")
fig = plt.figure(figsize=(15, 5))
ax = plt.subplot(2, 1,2)

ax.boxplot(df_up['quantity'], False, sym='rs', vert=False, whis=0.5, positions=[0], widths=[0.3])

plt.tight_layout()
plt.show()

Проверка столбца price

In [22]:
df_up['price'].describe(percentiles=[0.1, 0.5, 0.6
, 0.7, 0.8, 0.88, 0.9, 0.95]).T
Out[22]:
count     4850.000000
mean       516.060206
std        946.179939
min          9.000000
10%         38.000000
50%        150.000000
60%        188.000000
70%        374.000000
80%        712.000000
88%       1124.000000
90%       1424.000000
95%       2198.750000
max      14917.000000
Name: price, dtype: float64
In [23]:
#Найдем заказ с максимальной ценой
df_up.sort_values(by='price', ascending=False)[:5]
Out[23]:
date customer_id order_id product quantity price revenue year quartel month week day hour
5992 2019-07-29 17:00:00 0d87f4ae-465a-4fac-81e6-5d629761783e 72139 Сушилка уличная Leifheit 85210 LINOMATIC V 400... 1 14917.0 14917.0 2019-01-01 3 2019-07-01 Monday 2019-07-29 17
2697 2019-04-05 19:00:00 c0c60544-3a99-49d0-8a8e-cf7f293c22cb 71035 Сумка-тележка хозяйственная Andersen Royal Sho... 1 8737.0 8737.0 2019-01-01 2 2019-04-01 Friday 2019-04-05 19
1981 2019-02-24 10:00:00 ac250053-a236-467a-97d2-ddbb9bf4a1ba 70423 Сумка-тележка хозяйственная Andersen Alu Star ... 1 8437.0 8437.0 2019-01-01 1 2019-02-01 Sunday 2019-02-24 10
2997 2019-04-21 16:00:00 19d904d8-8d16-476d-8f66-b2a3b7a23660 71227 Сумка-тележка хозяйственная Rolser MNB019 rojo... 1 8077.0 8077.0 2019-01-01 2 2019-04-01 Sunday 2019-04-21 16
6629 2019-10-16 15:00:00 d5584388-ffbe-42fd-a746-a98828ec919f 72992 Стремянка 7 ступенчатая Hailo 8040-707 XXL 13 ... 1 7724.0 7724.0 2019-01-01 4 2019-10-01 Wednesday 2019-10-16 15
In [24]:
# Построим диаграмму размаха("ящик с усами")
fig = plt.figure(figsize=(15, 5))
ax = plt.subplot(2, 1,2)

ax.boxplot(df_up['price'], False, sym='rs', vert=False, whis=0.5, positions=[0], widths=[0.3])

plt.tight_layout()
plt.show()

**Наблюдение:** В данных нашли оптовый заказ 71743, который удалили. По ценам никаких выбросов нет.

Проверка на уникальность заказов/клиентов¶

Проверим задвоение заказов на несколько покупателей

In [25]:
dupl_user = df_up.groupby('order_id').agg({'customer_id': 'nunique', 'revenue': 'sum', 'quantity': 'sum'}).reset_index()
dupl_user = dupl_user.query('customer_id > 1')
print('Всего задвоений покупателей:', len(dupl_user))
Всего задвоений покупателей: 29
In [26]:
data_check_dup= (df_up.groupby(['order_id', 'product']).agg({'customer_id': 'nunique'}).reset_index().query('customer_id > 1')['order_id'])
data_check_dup
Out[26]:
1657    14872
1658    14872
1659    14872
2331    68785
2819    69283
2839    69310
2854    69345
2884    69410
2924    69485
2957    69531
3092    69833
3226    70114
3471    70542
3523    70631
3570    70726
3609    70808
3659    70903
3679    70946
3741    71054
3829    71226
3936    71461
3945    71480
3970    71542
3987    71571
4031    71648
4035    71663
4299    72188
4601    72778
4608    72790
4642    72845
4709    72950
Name: order_id, dtype: int64
In [27]:
data = df_up.query('order_id not in @dupl_user')
data.shape
Out[27]:
(4850, 13)
In [28]:
data = df_up.query('order_id not in @data_check_dup')
data.shape
Out[28]:
(4784, 13)

**Наблюдение:** После удаления дубликатов в разрезе покупателей и заказов мы имеем 4784 проданных товаров.

После обработи данных выведим на экран новые данные

In [29]:
# Количество строк
print('Количесво строк таблицы: {}, данные после обработки уменьшилось на {:.2%}'.format(len(data), 1 - (len(data) / len(df))))
# Количество уникальных покупателей

print('Кол-во уникальных покупателей: {}, данные после обработки уменьшилось на {:.2%}'
.format(len(data['customer_id'].unique()), 1 - (len(data['customer_id'].unique()) / len(df['customer_id'].unique()))))

# Количество уникальных заказов
print('Количество уникальных заказов:{}, данные после обработки уменьшилось на {:.2%}'
.format(len(data['order_id'].unique()), 1 - (len(data['order_id'].unique()) / len(df['order_id'].unique()))))

# Количество проданного товара
print('Количество проданного товара:{}, данные после обработки уменьшилось на {:.2%}'
.format(data['quantity'].sum(), 1 - ((data['quantity'].sum()) / (df['quantity'].sum()))))
      
# Общая выручка 
print('Общая выручка:{}, данные после обработки уменьшилось на {:.2%}'
.format(data['revenue'].sum(), 1 - (data['revenue'].sum() / df['revenue'].sum())))

# Средний чек заказа
order_sum = df.groupby('order_id').agg({'revenue': 'sum'}).reset_index()
order_sum_after = data.groupby('order_id').agg({'revenue': 'sum'}).reset_index()
print('Средний чек заказа:{}, данные после обработки уменьшилось на {:.2%}'
.format(order_sum_after['revenue'].mean(), 1 - (order_sum_after['revenue'].mean()) / (order_sum['revenue'].mean())))
Количесво строк таблицы: 4784, данные после обработки уменьшилось на 28.99%
Кол-во уникальных покупателей: 2393, данные после обработки уменьшилось на 2.37%
Количество уникальных заказов:2754, данные после обработки уменьшилось на 1.08%
Количество проданного товара:12437, данные после обработки уменьшилось на 26.20%
Общая выручка:3411515.0, данные после обработки уменьшилось на 29.68%
Средний чек заказа:1238.7490922294844, данные после обработки уменьшилось на 28.91%
In [30]:
data.shape
Out[30]:
(4784, 13)

ВЫВОД:

В исходном датасете 6737 строк и 6 столбцов. После чистки данных и добавление столбцов получилось 4784 строк и 13 столбцов. После того, как изучили датасеты, выявили следующие первичные отклонения: 1) Изменили тип данных в столбце `date` и добавили новые столбцы: * `year`- год; * `quartel`- квартал; * `month`- месяц; * `week`- день недели; * `day`- день; * `hour`- час 2) Добавили столбец `revenue` с данными о выручке по каждому товару.\ 3) Удалили выброс по количеству товара (заказ 71743). 4) Удалили 28,97% данных, где произошло задвоение по покупателям и заказам.
In [31]:
# Выгрузим очищенный датафрэйм для табло

data.to_csv('data.csv', index = False)

Анализ данных(EDA)¶

Количество товаров в разрезе заказов¶

Эти данные нам помогут распределить товары на группы

In [32]:
# Группируем данные по количеству заказов
quan_count = data.groupby('quantity').agg({'order_id': 'count'}).reset_index()
# Переименовываем название столбцов
quan_count.columns = ['quantity','count']
# Добавляем столбец "%", чтобы найти долю с общего объема
quan_count['%'] = round((quan_count['count']/quan_count['count'].sum())*100,2)
# Сортируем данные по убыванию
quan_count[:10].sort_values(by='count', ascending=False).style.format({'%':'{:.2f}%'})

#quan_count[:10].style.bar(subset=['count'], color='#ffe135')
Out[32]:
  quantity count %
0 1 3758 78.55%
1 2 423 8.84%
2 3 138 2.88%
3 4 101 2.11%
9 10 70 1.46%
4 5 67 1.40%
5 6 41 0.86%
6 7 20 0.42%
7 8 14 0.29%
8 9 7 0.15%

**Наблюдение:** Основной объем продаж состоит из 1-2 позиций в заказе.

Разбиваем данные на группы для визуализации:

  • A: 1 товар
  • B: 2 товара
  • C: 3-10 товаров
  • D: 11-30 товаров
  • E: 31-50 товаров
  • F: < 51 товара
In [33]:
def group_goods(product):
    try:
        if  1 == product:
            return 'A'
        elif 2 == product:
            return 'B'
        elif 3 <= product <= 10:
            return 'C'
        elif 11 <= product <= 30:
            return 'D'
        elif 31 <= product <= 50:
            return 'E'
        elif product >= 51:
            return 'F'
    except:
        pass
quan_count['category_quantity'] = quan_count['quantity'].apply(group_goods)
quan_count[:10];
In [34]:
 # Группируем данные по количеству заказов
categoties_goods = quan_count.groupby('category_quantity').agg({'count': 'sum'}).reset_index()
# Переименовываем название столбцов
categoties_goods.columns = ['category_quantity','count']
# Добавляем столбец "%", чтобы найти долю с общего объема
categoties_goods['%'] = round((categoties_goods['count']/categoties_goods['count'].sum())*100,1)
# Сортируем данные по убыванию
categoties_goods.sort_values(by='count', ascending=False)

categoties_goods.style.bar(subset=['count'], color='#ffe135')
Out[34]:
  category_quantity count %
0 A 3758 78.600000
1 B 423 8.800000
2 C 458 9.600000
3 D 109 2.300000
4 E 14 0.300000
5 F 22 0.500000
In [35]:
# Построим круговую диаграмму для определения доли каждого события 
fig = go.Figure(data=[go.Pie(labels=quan_count['category_quantity'], values=quan_count['%'])])
fig.update_layout(title="Распределение количества заказов по группах (%)"
)
fig.update_traces(hoverinfo='label+percent', textinfo='value', textfont_size=10, marker=dict(line=dict(color='#000000', width=1)))
fig.show()

**Наблюдение:** Более 78% в одном заказе присутствует только 1 товар, что можно оценить, как неэффективность работы отдела продаж или отдельно продавцов. Также присутствуют заказы, где более 50 товаров в заказе, есть предположение, что это были или оптовые покупатели или технический сбой в программе.

Количество уникальных покупателей в разрезе месяца, дня недели и времени¶

Количество уникальных покупателей в разрезе месяца¶

In [36]:
 # Группируем данные по месяцам
revenue_month = data.groupby('month').agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
# Переименовываем название столбцов
revenue_month.columns = ['month','orders_Q', 'orders_revenue']

revenue_month.style.bar(subset=['orders_revenue'], color='#ffe135')
Out[36]:
  month orders_Q orders_revenue
0 2018-10-01 00:00:00 478 350374.000000
1 2018-11-01 00:00:00 430 359772.000000
2 2018-12-01 00:00:00 303 341910.000000
3 2019-01-01 00:00:00 184 234117.000000
4 2019-02-01 00:00:00 359 304446.000000
5 2019-03-01 00:00:00 407 249537.000000
6 2019-04-01 00:00:00 673 316122.000000
7 2019-05-01 00:00:00 685 228814.000000
8 2019-06-01 00:00:00 324 233624.000000
9 2019-07-01 00:00:00 311 226361.000000
10 2019-08-01 00:00:00 200 180910.000000
11 2019-09-01 00:00:00 216 177922.000000
12 2019-10-01 00:00:00 214 207606.000000
In [37]:
# Найдем среднюю выручку 
month_aver = round(revenue_month['orders_revenue'].sum()/12, 2)
print('Средняя выручка в месяц в разрезе года:', month_aver)
Средняя выручка в месяц в разрезе года: 284292.92
In [38]:
# Построим график
fig = px.bar(revenue_month, y='orders_revenue', x= 'month') 
# оформляем график
fig.update_layout(title='Ежемесячная выручка(руб.)',
                   xaxis_title='Месяц',
                   yaxis_title='Выручка(руб.)',
                   width=800, # указываем размеры графика
                   height=500)
# добавляем ось X 
fig.add_hline(y=month_aver, line_dash="dash", line_color="grey")

fig.show()

Найдем среднее количество заказов и среднюю выручку по месяцам

In [39]:
# Сгруппируем данные
month_user = data.groupby('month')['customer_id'].agg(['nunique']).reset_index()
# Объединим таблицы по месяцу
user_aver = month_user.merge(revenue_month, on = ['month'])
# Добавим столбец со сред кол заказов на одного покупателя
user_aver['Среднее кол-во заказов'] = (user_aver['orders_Q'] / user_aver['nunique']).round(2)
# Добавим столбец со сред выручной на одного покупателя
user_aver['Средняя сумма заказа пок-ля'] = (user_aver['orders_revenue'] /user_aver['nunique']).round(2)
user_aver.columns = ['Месяц','Q покупателей','Q заказов', 'Выручка', 'Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля']
user_aver
Out[39]:
Месяц Q покупателей Q заказов Выручка Среднее кол-во заказов пок-ля Средняя выручка заказа пок-ля
0 2018-10-01 178 478 350374.0 2.69 1968.39
1 2018-11-01 178 430 359772.0 2.42 2021.19
2 2018-12-01 226 303 341910.0 1.34 1512.88
3 2019-01-01 151 184 234117.0 1.22 1550.44
4 2019-02-01 244 359 304446.0 1.47 1247.73
5 2019-03-01 228 407 249537.0 1.79 1094.46
6 2019-04-01 235 673 316122.0 2.86 1345.20
7 2019-05-01 162 685 228814.0 4.23 1412.43
8 2019-06-01 150 324 233624.0 2.16 1557.49
9 2019-07-01 185 311 226361.0 1.68 1223.57
10 2019-08-01 165 200 180910.0 1.21 1096.42
11 2019-09-01 171 216 177922.0 1.26 1040.48
12 2019-10-01 169 214 207606.0 1.27 1228.44

**Наблюдение:** Объем выручки пришелся на июнь, хотя сентябрь показал "плохой" месяц по выручке. Лидерами по среднему количеству заказов оказались месяца- май и апрель. Лидерами по среднему чеку стали- июнь и ноябрь.

Количество уникальных покупателей в разрезе дня недели¶

In [40]:
# Группируем данные по дням недели
revenue_week = data.groupby('week').agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
# Переименовываем название столбцов
revenue_week.columns = ['week','orders_Q', 'orders_revenue']

revenue_week.style.bar(subset=['orders_revenue'], color='#ffe135')
Out[40]:
  week orders_Q orders_revenue
0 Friday 600 507445.000000
1 Monday 894 584357.000000
2 Saturday 499 260282.000000
3 Sunday 517 337947.000000
4 Thursday 756 537790.000000
5 Tuesday 775 676060.000000
6 Wednesday 743 507634.000000

Найдем среднее количество заказов и среднюю выручку по дням недели

In [41]:
# Сгруппируем данные
week_user = data.groupby('week')['customer_id'].agg(['nunique']).reset_index()
# Объединим таблицы по месяцу
user_aver_week = week_user.merge(revenue_week, on = ['week'])
# Добавим столбец со сред кол заказов на одного покупателя
user_aver_week['Среднее кол-во заказов'] = (user_aver_week['orders_Q'] / user_aver_week['nunique']).round(2)
# Добавим столбец со сред выручной на одного покупателя
user_aver_week['Средняя сумма заказа пок-ля'] = (user_aver_week['orders_revenue'] /user_aver_week['nunique']).round(2)
user_aver_week.columns = ['Месяц','Q покупателей','Q заказов', 'Выручка', 'Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля']
user_aver_week.style.bar(subset=['Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля'], color='#ffe135')
Out[41]:
  Месяц Q покупателей Q заказов Выручка Среднее кол-во заказов пок-ля Средняя выручка заказа пок-ля
0 Friday 320 600 507445.000000 1.880000 1585.770000
1 Monday 430 894 584357.000000 2.080000 1358.970000
2 Saturday 234 499 260282.000000 2.130000 1112.320000
3 Sunday 325 517 337947.000000 1.590000 1039.840000
4 Thursday 387 756 537790.000000 1.950000 1389.640000
5 Tuesday 429 775 676060.000000 1.810000 1575.900000
6 Wednesday 382 743 507634.000000 1.950000 1328.880000

**Наблюдение:** Объем выручки пришелся на вторник, а суббота показала "плохой" день недели по выручке. Лидерами по среднему количеству заказов оказались дни недели- воскресенье и понедельник. Лидерами по среднему чеку стали- вторник и пятница.

Количество уникальных покупателей в разрезе времени¶

In [42]:
# Группируем данные по времени
revenue_hours = data.groupby('hour').agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
# Переименовываем название столбцов
revenue_hours.columns = ['hour','orders_Q', 'orders_revenue']

revenue_hours.style.bar(subset=['orders_revenue'], color='#ffe135')
Out[42]:
  hour orders_Q orders_revenue
0 0 42 31392.000000
1 1 41 21491.000000
2 2 16 22405.000000
3 3 8 6415.000000
4 4 28 18937.000000
5 5 36 19232.000000
6 6 47 53945.000000
7 7 88 146112.000000
8 8 281 179326.000000
9 9 328 229504.000000
10 10 343 218823.000000
11 11 401 282890.000000
12 12 323 237150.000000
13 13 405 304536.000000
14 14 386 205529.000000
15 15 326 243530.000000
16 16 327 220045.000000
17 17 270 243551.000000
18 18 165 160943.000000
19 19 216 147099.000000
20 20 197 117169.000000
21 21 240 147158.000000
22 22 188 118679.000000
23 23 82 35654.000000
In [82]:
# Построим график
fig = px.bar(revenue_hours, y='orders_revenue', x= 'hour') 
# оформляем график
fig.update_layout(title='Часовая выручка(руб.)',
                   xaxis_title='Час',
                   yaxis_title='Выручка(руб.)',
                   width=1000, # указываем размеры графика
                   height=500)
# добавляем ось X 
#fig.add_hline(y=month_aver, line_dash="dash", line_color="grey")

fig.show()

Найдем среднее количество заказов и среднюю выручку по часам

In [43]:
# Сгруппируем данные по времени
hour_user = data.groupby('hour')['customer_id'].agg(['nunique']).reset_index()
# Объединим таблицы по месяцу
user_aver_hour = hour_user.merge(revenue_hours, on = ['hour'])
# Добавим столбец со сред кол заказов на одного покупателя
user_aver_hour['Среднее кол-во заказов'] = (user_aver_hour['orders_Q'] / user_aver_hour['nunique']).round(2)
# Добавим столбец со сред выручной на одного покупателя
user_aver_hour['Средняя сумма заказа пок-ля'] = (user_aver_hour['orders_revenue'] /user_aver_hour['nunique']).round(2)
user_aver_hour.columns = ['Час','Q покупателей','Q заказов', 'Выручка', 'Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля']
user_aver_hour.style.bar(subset=['Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля'], color='#ffe135')
Out[43]:
  Час Q покупателей Q заказов Выручка Среднее кол-во заказов пок-ля Средняя выручка заказа пок-ля
0 0 30 42 31392.000000 1.400000 1046.400000
1 1 15 41 21491.000000 2.730000 1432.730000
2 2 13 16 22405.000000 1.230000 1723.460000
3 3 8 8 6415.000000 1.000000 801.880000
4 4 17 28 18937.000000 1.650000 1113.940000
5 5 18 36 19232.000000 2.000000 1068.440000
6 6 32 47 53945.000000 1.470000 1685.780000
7 7 62 88 146112.000000 1.420000 2356.650000
8 8 121 281 179326.000000 2.320000 1482.030000
9 9 167 328 229504.000000 1.960000 1374.280000
10 10 195 343 218823.000000 1.760000 1122.170000
11 11 201 401 282890.000000 2.000000 1407.410000
12 12 190 323 237150.000000 1.700000 1248.160000
13 13 191 405 304536.000000 2.120000 1594.430000
14 14 186 386 205529.000000 2.080000 1104.990000
15 15 175 326 243530.000000 1.860000 1391.600000
16 16 149 327 220045.000000 2.190000 1476.810000
17 17 144 270 243551.000000 1.880000 1691.330000
18 18 106 165 160943.000000 1.560000 1518.330000
19 19 124 216 147099.000000 1.740000 1186.280000
20 20 119 197 117169.000000 1.660000 984.610000
21 21 142 240 147158.000000 1.690000 1036.320000
22 22 102 188 118679.000000 1.840000 1163.520000
23 23 49 82 35654.000000 1.670000 727.630000

**Наблюдение:** Объем заказов пришелся на промежуток времени с 8 до 17 часов, большая часть покупателей пришлась на промежуток времени с 9 до 15 часов. Наибольшая выручка пришлась на 15 часов.

Количество заказов в разрезе дня недели и времени¶

In [44]:
# Сгруппируем данные по дню
day_order = data.groupby(['day']).agg({'order_id':'nunique', 'revenue': 'sum'}).reset_index()
# Переименовываем название столбцов
day_order.columns = ['day', 'Q_orders','sum_orders']
day_order.head()
Out[44]:
day Q_orders sum_orders
0 2018-10-01 9 8261.0
1 2018-10-02 14 16746.0
2 2018-10-03 8 10574.0
3 2018-10-04 11 25170.0
4 2018-10-05 6 4036.0

Построим гистограмму распределения заказов

In [45]:
day_orders_sum = data.groupby(['order_id']).agg({'revenue': 'sum'}).reset_index()
day_orders_sum.columns = ['orders', 'count_sum']
day_orders_sum.head()
Out[45]:
orders count_sum
0 12624 375.0
1 13547 684.0
2 14480 359.0
3 14481 600.0
4 14482 376.0
In [46]:
# Просмотр сводной статистики
day_orders_sum['count_sum'].reset_index().describe(percentiles=[0.1, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99]).T
Out[46]:
count mean std min 10% 50% 60% 70% 80% 90% 95% 99% max
index 2754.0 1376.500000 795.155645 0.0 275.3 1376.5 1651.8 1927.1 2202.4 2477.7 2615.35 2725.47 2753.0
count_sum 2754.0 1238.749092 2239.602191 14.0 134.0 686.0 899.0 1162.0 1705.8 2774.0 3908.10 7792.62 49432.0
In [47]:
# Построим гистограмму по дате

fig = px.histogram(day_orders_sum.query('count_sum <= 8000'), x="count_sum", nbins=50)
fig.update_layout(title_text='Распределение суммы заказов', xaxis_title="Сумма заказа", yaxis_title="Количество заказов")
fig.show()

**Наблюдение:** Согласно гистограмме видно, что 90% заказов ниже 2770 руб., 50% заказов ниже 690 руб.

Лидеры продаж топ-10 товаров¶

In [48]:
top10_goods = data.groupby(['product']).agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
top10_goods.sort_values(by=['revenue'], ascending=False)[:10]
Out[48]:
product order_id revenue
1188 Простынь вафельная 200х180 см WELLNESS RW180-0... 2 53232.0
1611 Сумка-тележка 2-х колесная Gimi Argo синяя 47 50405.0
190 Вешалки мягкие для деликатных вещей 3 шт шоколад 2 49596.0
1888 Тележка багажная DELTA ТБР-22 синий грузоподъе... 3 33992.0
831 Муляж ЯБЛОКО 9 см красное 6 32702.0
870 Набор ножей Attribute CHEF 5 предметов AKF522 1 29248.0
1637 Сумка-тележка хозяйственная Andersen Scala Sho... 5 28045.0
1939 Урна уличная "Гео", Hobbyka/Хоббика, 59*37,5см... 1 24370.0
131 Веник сорго с деревянной ручкой с 4-мя швами, ... 2 20010.0
1622 Сумка-тележка 3-х колесная Gimi Tris Floral синяя 7 18893.0

**Наблюдение:** Наибольший доход принесли такие позиции, как простынь вафельная, сумка-тележка и вешалки мягкие.

Аутсайдеры продаж топ-10 товаров¶

In [49]:
out10_goods = data.groupby(['product']).agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
out10_goods.sort_values(by=['revenue'], ascending=True)[:10]
Out[49]:
product order_id revenue
731 Львиный зев Волшебный ковер 0,05 г 4660010779639 1 9.0
313 Горох Амброзия 10,0 г 4660010772616 1 9.0
2054 Цинния Коралловая красавица 0,2 г 4660010773323 1 10.0
940 Огурец Засолочный 0,3 г 4660010776102 1 10.0
909 Незабудка смесь 0,1 г 4650091480340 1 10.0
2055 Цинния Оранжевый король 0,5 г 4660010770520 1 10.0
766 Морковь Детская сладость 2 г 4660010775921 1 10.0
1053 Петрушка Итальянский гигант 2 г 4660010776553 1 10.0
693 Лаватера Монт Блан 0,3 г 4660010778588 1 11.0
433 Календула Суприм 0,5 г 4650091480227 1 11.0

**Наблюдение:** Неспросовыми товарами оказались волшебный ковер, горох Амброзия, Цинния Коралловая и огурец. Возможно стоит их убрать из ассортимента.

Детализация исследования: анализ товарного ассортимента¶

Сгруппируем данные по категориям и найдем топ-10¶

In [50]:
# Заменим названия товаров
data['product'] = data['product'].str.replace('Мусорный контейнер в ванную комнату BOWL-SHINY полистирол 14х16 см белый, Spirella, 1014964',
 'Контейнер мусорный в ванную комнату BOWL-SHINY полистирол 14х16 см белый, Spirella, 1014964')
data['product'] = data['product'].str.replace('Этажерка цветочная пластиковая ЛИВИЯ 5 кашпо М-пластика M3140',
 'Цветочная этажерка пластиковая ЛИВИЯ 5 кашпо М-пластика M3140')
data['product'] = data['product'].str.replace('Ящик почтовый металлический с врезным замком Почта 1205250',
 'Почтовый ящик металлический с врезным замком Почта 1205250')
data['product'] = data['product'].str.replace('Набор ковров для ванной комнаты, "Офелия", 40х50/50х80см., серый/бежевый, NTBM50804050-26-190',
 'Ковры набор для ванной комнаты,"Офелия", 40х50/50х80см., серый/бежевый, NTBM50804050-26-190')

Выдедим первое слово в новый столбец first_name

In [51]:
data['first_name'] = data['product'].apply(lambda x: x.split(' ')[0])
# Приведем к нижнему регистру
data['first_name'] = data['first_name'].str.lower()
data[['first_name','product']].head(10)
Out[51]:
first_name product
0 комнатное Комнатное растение в горшке Алое Вера, d12, h30
1 комнатное Комнатное растение в горшке Кофе Арабика, d12,...
2 радермахера Радермахера d-12 см h-20 см
3 хризолидокарпус Хризолидокарпус Лутесценс d-9 см
4 циперус Циперус Зумула d-12 см h-25 см
5 шеффлера Шеффлера Лузеана d-9 см
6 юкка Юкка нитчатая d-12 см h-25-35 см
7 настенная Настенная сушилка для белья Gimi Brio Super 100
8 таз Таз пластмассовый 21,0 л круглый "Водолей" С61...
9 чехол Чехол для гладильной доски Colombo Persia Beig...

После обработки данных в эксель, составляем список, который позволит распределить товара по категориям

In [52]:
# 1 Все для цветов
flowers = [
 'примула','калибрахоа','фуксия','вербена','пуансеттия','фиалка',
'дыня','комнатное','базилик','бегония','бальзамин',
 'бакопа','космея','мята','папоротник','календула', 'каланхое',
'тыква', 'гербера','цветущее', 'афеляндра',
 'цитрофортунелла', 'пахира', 'фаленопсис', 'искусственная', 'эхеверия', 'клубника', 'многолетнее', 'кофе',
 'седум', 'табак', 'спатифиллум', 'дендориум', 'калла', 'лавр', 'мирт','львиный', 'дендробиум', 'цветочная',
 'искусственный','колокольчик','декоративная','подвесное', 'нивянник', 'вербейник', 'гардения', 'гортензия', 
 'калатея', 'алое', 'кореопсис', 'укроп', 'вигна', 'скиммия',
 'колеус', 'душица', 'фатсия', 'лантана', 'кабачок','антуриум','огурец','хризантема','эвкалипт','декабрист',
 'томат','гвоздика','арбуз','петрушка','цинния', 'патиссон','алиссум','азалия','тимьян', 'лобелия', 'исскуственная', 
 'капуста', 'газания','циперус','виола', 'хлорофитум', 'лаванда', 'розмарин', 'мимоза','мединилла', 'тагетис', 'земляника', 
 'астра','пеларгония','рассада','томата','пеларгония', 'роза','петуния', 'герань', 'цветок','однолетнее','флокс','цикламен',' зверобой', 'настурция',  'салат', 'осина', 'целозия', 'портулак', 'крассула',
 'аргирантерум', 'хоста', 'цинерария', 'монарда', 'баклажан', 'вероника', 'сальвия', 'кориандр','лен']

# 2 Все для ванной
bathroom = ['ковры','сетка', 'ванна', 'подголовник', 'штора',  'полотенце', 'махровое','вешалка-сушилка', 'карниз','настенная','сушилка','фен','штанга','ёрш','ерш','ковш','пробка','комплект','контейнер', 'сиденье','корзина',
 'бак','стакан','контейнер','халат', 'махровый']

# 3 Все для кухни
kitchen = ['мантоварка-пароварка', 'мантоварка','электроштопор', 'овощечистка','мусорный','кисточка','крышка',
'набор','просеиватель','лоток','емкость', 'тарелка', 'сахарница','салатник','кружка','банка','нож','сковорода','кастрюля','ложка','вилка', 'кухонное',
 'термокружка', 'термос', 'нож', 'кувшин', 'чайный', 'миска','овсянница', 'терка','измельчитель', 'чайная', 'разделочная', 'блюдце', 'рыбочистка', 'термостакан', 'бидон', 'половник', 'толкушка',
 'сервировочная', 'лопатка', 'столовый', 'сахарница', 'сотейник',
'бульонница', 'венчик', 'скалка', 'сотейник','тортница', 'tepмокружка', 'хлебница', 'блюдо', 'чайник', 'модульная',
 'отделитель','весы', 'миксер', 'овощеварка', 'соковарка','котел']

# 4 Товары для хранения вещей
storage = ['полки', 'этажерка','полка', 'обувница-3', 'складной', 'вешалка','вешалки', 'вешалка-плечики', 'сумка',
 'вешалка-стойка', 'комод', 'стеллаж', 'подставка', 'плечики']

# 5 Хозтовары и инвентарь
household = ['лестница-стремянка','тележка','стремянка', 'лестница', 'стремянки', 'урна-пепельница', 'муссорный',
 'урна', 'стремянка-табурет', 'почтовый','сумка-тележка','таз', 'вантуз', 'швабра','ведро','щетка-утюжок',
 'жестяная', 'зубная', 'шнур', 'петля', 'ящик', 'коробка', 'бельевые','новогоднее', 'кондиционер',
 'корыто', 'веник', 'мыло', 'дозатор', 'шило', 'термометр',
'тряпкодержатель',
 'совок', 'бензин', 'мешок', 'шпагат', 'вакумный', 'фал', 'корзинка', 'ваза',
 'подарочный', 'ткань','автоматическая', 'пылесос','щетка-сметка'
, 'окномойка','щётка','щетка','паста','салфетка','насадка',
 'cредство','перчатки', 'муляж', 'измерительный','сверло-фреза','крепеж',
 'многофункциональный', 'петля-стрела','крючок','камнеломка','стяжка', 'стяжки',]

# 6 Все для спальни
bedroom = ['покрывало', 'простыня', 'одеяло', 'светильник','простынь', 'подушка', 'наматрасник', 'двуспальное',
 'наматрацник-чехол', 'наматрацник', 'пододеяльник','наматрицник', 'чехол', 'кофр','плед','ковер', 'ковёр', 'коврик','скатерть','утюг', 'подкладка', 'подрукавник', 'рукав', 'гладильная', 'чехол'
,'покрытие','доска']

Далее мы с помощью функции распределим все товары по категориям

In [53]:
def categorie (row):
     if row['first_name'] in flowers:
        return 'Все для цветов'
     if row['first_name'] in bathroom:
        return 'Все для ванной'
     if row['first_name'] in kitchen:
        return 'Все для кухни'
     if row['first_name'] in storage:
        return 'Товары для хранения вещей'
     if row['first_name'] in household:
        return 'Хозтовары и инвентарь'
     if row['first_name'] in bedroom:
        return 'Все для спальни'
     else:
        return 'Другое'
# Добавим категории в новый столбец
data['categories_product'] = data.apply(categorie, axis=1)
In [54]:
data.head()
Out[54]:
date customer_id order_id product quantity price revenue year quartel month week day hour first_name categories_product
0 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Алое Вера, d12, h30 1 142.0 142.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0 комнатное Все для цветов
1 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Кофе Арабика, d12,... 1 194.0 194.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0 комнатное Все для цветов
2 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Радермахера d-12 см h-20 см 1 112.0 112.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0 радермахера Другое
3 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Хризолидокарпус Лутесценс d-9 см 1 179.0 179.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0 хризолидокарпус Другое
4 2018-10-01 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Циперус Зумула d-12 см h-25 см 1 112.0 112.0 2018-01-01 4 2018-10-01 Monday 2018-10-01 0 циперус Все для цветов
In [55]:
# Группируем данные по категориям
cat = data.groupby('categories_product').agg({'product': 'count'}).reset_index()\
                                        .sort_values(by='product', ascending=False)
# Добавляем столбец "%", чтобы найти долю с общего объема по категориям
cat ['%'] = round((cat['product']/cat['product'].sum())*100,1)

# Переименовываем название столбцов
cat.columns = ['Категория','Кол-во товара', 'Доля(%)']
cat.style.bar(subset=['Доля(%)'], color='#ffe135')
Out[55]:
  Категория Кол-во товара Доля(%)
3 Все для цветов 2431 50.800000
6 Хозтовары и инвентарь 817 17.100000
0 Все для ванной 481 10.100000
2 Все для спальни 415 8.700000
1 Все для кухни 311 6.500000
4 Другое 221 4.600000
5 Товары для хранения вещей 108 2.300000
In [56]:
cat.plot(y= 'Доля(%)', kind="pie", figsize=(7, 7), autopct='%1.1f%%', labels=cat['Категория'],)
plt.legend(bbox_to_anchor=(0.6, 0, 0.6, 0.9)) # Расположение легенды на графике
plt.title('Доля распределения категорий товара') # Название графика
plt.show()

**Наблюдение:** Согласно диаграмме, основной категорией с долей продаж в 50,7% стали товары в категории "Все для цветов", далее 17,1%- "Хозтовары и инвентарь" и 10,1%- "Все для ванной".

Средняя выручка по категориям и месяцам¶

In [57]:
# Объединим таблицы по месяцу
user_aver = month_user.merge(revenue_month, on = ['month'])
# Добавим столбец со сред кол заказов на одного покупателя
user_aver['Среднее кол-во заказов'] = (user_aver['orders_Q'] / user_aver['nunique']).round(2)
# Добавим столбец со сред выручной на одного покупателя
user_aver['Средняя сумма заказа пок-ля'] = (user_aver['orders_revenue'] /user_aver['nunique']).round(2)
user_aver.columns = ['Месяц','Q покупателей','Q заказов', 'Выручка', 'Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля']
user_aver
Out[57]:
Месяц Q покупателей Q заказов Выручка Среднее кол-во заказов пок-ля Средняя выручка заказа пок-ля
0 2018-10-01 178 478 350374.0 2.69 1968.39
1 2018-11-01 178 430 359772.0 2.42 2021.19
2 2018-12-01 226 303 341910.0 1.34 1512.88
3 2019-01-01 151 184 234117.0 1.22 1550.44
4 2019-02-01 244 359 304446.0 1.47 1247.73
5 2019-03-01 228 407 249537.0 1.79 1094.46
6 2019-04-01 235 673 316122.0 2.86 1345.20
7 2019-05-01 162 685 228814.0 4.23 1412.43
8 2019-06-01 150 324 233624.0 2.16 1557.49
9 2019-07-01 185 311 226361.0 1.68 1223.57
10 2019-08-01 165 200 180910.0 1.21 1096.42
11 2019-09-01 171 216 177922.0 1.26 1040.48
12 2019-10-01 169 214 207606.0 1.27 1228.44
In [58]:
# Сгруппируем данные  
month_category = data.pivot_table(index='categories_product', columns= 'month', values=['revenue'],\
               aggfunc={'revenue': 'sum' }).reset_index()

cm = sns.light_palette("#5f9bd6", as_cmap=True)

month_category.style.background_gradient(cmap=cm)
Out[58]:
  categories_product revenue
month NaT 2018-10-01 00:00:00 2018-11-01 00:00:00 2018-12-01 00:00:00 2019-01-01 00:00:00 2019-02-01 00:00:00 2019-03-01 00:00:00 2019-04-01 00:00:00 2019-05-01 00:00:00 2019-06-01 00:00:00 2019-07-01 00:00:00 2019-08-01 00:00:00 2019-09-01 00:00:00 2019-10-01 00:00:00
0 Все для ванной 73656.000000 35282.000000 47196.000000 37088.000000 59539.000000 47284.000000 22512.000000 13439.000000 31700.000000 52218.000000 22573.000000 28983.000000 44970.000000
1 Все для кухни 34313.000000 69663.000000 32233.000000 29623.000000 41182.000000 23787.000000 7217.000000 9007.000000 4255.000000 9889.000000 12920.000000 17514.000000 5287.000000
2 Все для спальни 66237.000000 71451.000000 93730.000000 84394.000000 46685.000000 33245.000000 43992.000000 13083.000000 52599.000000 50311.000000 17166.000000 38089.000000 23456.000000
3 Все для цветов 30308.000000 19777.000000 11551.000000 23833.000000 38669.000000 43285.000000 95417.000000 106029.000000 37308.000000 29282.000000 24726.000000 19411.000000 21243.000000
4 Другое 16156.000000 6239.000000 10942.000000 8350.000000 7358.000000 8450.000000 5903.000000 2910.000000 2762.000000 2278.000000 3397.000000 7895.000000 5479.000000
5 Товары для хранения вещей 6479.000000 10889.000000 7635.000000 10529.000000 11882.000000 14318.000000 10773.000000 7723.000000 56205.000000 6339.000000 15483.000000 17523.000000 16225.000000
6 Хозтовары и инвентарь 123225.000000 146471.000000 138623.000000 40300.000000 99131.000000 79168.000000 130308.000000 76623.000000 48795.000000 76044.000000 84645.000000 48507.000000 90946.000000

Соотношение основых и дополнительных товаров¶

  • Перед началом детализации каждой категории с разделением на основные и доптовары, мы выделим таблицы для каждой категории.
  • Сгруппируем данные по наименованию товара и месяца.
  • Далее посчитаем частоту покупок в год в разрезе месяца

1. Проанализируем категорию "Все для цветов"

In [59]:
# Создадим таблицу
flowers_tab = data.query('categories_product == "Все для цветов"')

# Сгруппируем данные по наименованию товара и месяцу

tab_1 = flowers_tab.pivot_table(index=['first_name', 'month'], values='price', aggfunc='median')\
        .sort_values(by='price',ascending=False)\
        .reset_index()
        #.rename(columns={'first_name':'Источник','acquisition_cost': 'CAC'})\
        #.round(2)
tab_1[:10]
Out[59]:
first_name month price
0 гортензия 2019-09-01 3599.0
1 афеляндра 2018-10-01 3524.0
2 цитрофортунелла 2019-04-01 3074.0
3 эвкалипт 2019-06-01 1762.0
4 эвкалипт 2019-03-01 1762.0
5 эвкалипт 2019-02-01 1409.0
6 эвкалипт 2018-10-01 1409.0
7 скиммия 2018-12-01 1139.0
8 пахира 2019-10-01 1087.0
9 мединилла 2019-09-01 1034.0

Посчитаем частоту покупок

In [60]:
# Сгруппируем данные по наименованию и посчитаем частоту покупок по каждой позиции

flowers_freq = tab_1.pivot_table(index=['first_name'], values='month', aggfunc='count')\
                      .reset_index()

# Выделим топ-10 самых продаваемых позиций в теч года
flowers_freq_year = flowers_freq.query('month >8').sort_values(by='month',ascending=False).head(10)
flowers_freq_year
Out[60]:
first_name month
20 герань 13
67 пеларгония 13
74 рассада 12
75 роза 12
30 искусственный 11
97 цветок 11
58 мята 9
91 фиалка 9

**Наблюдение:**

За основной товар в категории "Все для цветов" можно взять товар, который продавался каждый месяц.

  • Пеларгония и герань - 12 месяцев
  • Роза и рассада - 11 месяцев
  • Цветок исскуственный - 10 месяцев

2. Проанализируем категорию "Все для ванной"

In [61]:
# Создадим таблицу
bathroom_tab = data.query('categories_product == "Все для ванной"')
# Сгруппируем данные по наименованию товара и месяцу
tab_2 = bathroom_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
        .sort_values(by='order_id',ascending=False)\
        .reset_index()
        
tab_2
Out[61]:
first_name order_id price
0 сушилка 267 637.0
1 штора 76 974.0
2 корзина 33 592.0
3 ёрш 15 56.0
4 контейнер 15 674.0
5 карниз 14 224.0
6 сиденье 8 374.0
7 штанга 8 974.0
8 комплект 7 178.0
9 стакан 6 217.0
10 настенная 5 824.0
11 полотенце 4 89.0
12 махровое 4 130.5
13 подголовник 3 322.0
14 ковш 3 64.0
15 пробка 2 66.5
16 сетка 2 258.5
17 ерш 2 2661.5
18 махровый 1 1949.0
19 ванна 1 749.0
20 ковры 1 599.0
21 фен 1 592.0
22 халат 1 1949.0
23 вешалка-сушилка 1 1087.0
24 бак 1 3749.0

**Наблюдение:**

Распределить категорию "Все для ванной" можно следующим образом:

Основной:

  • Ванна
  • Шторы и карнизы
  • Сушилки полотенец
  • Корзины для белья

Дополнительный:

  • Коврики для ванной
  • Держатели для полотенец
  • Полотенца
  • Халат
  • Фен
  • Пробка

3. Проанализируем категорию "Все для кухни"

In [62]:
# Создадим таблицу
kitchen_tab = data.query('categories_product == "Все для кухни"')

# Сгруппируем данные по наименованию товара и месяцу
tab_3 = kitchen_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
        .sort_values(by='order_id',ascending=False)\
        .reset_index()
        
tab_3
Out[62]:
first_name order_id price
0 тарелка 39 97.0
1 набор 36 405.0
2 салатник 22 104.5
3 чайник 19 749.0
4 банка 17 164.0
5 нож 14 123.0
6 кружка 14 89.0
7 сковорода 9 712.0
8 ложка 9 142.0
9 термокружка 8 524.0
10 кувшин 8 149.0
11 вилка 7 187.0
12 кастрюля 6 1368.0
13 весы 6 1274.0
14 термос 5 554.0
15 чайный 5 802.0
16 миска 4 44.0
17 терка 4 246.5
18 овсянница 4 97.0
19 хлебница 4 693.0
20 tepмокружка 4 2361.5
21 измельчитель 4 479.0
22 лоток 4 471.5
23 емкость 3 265.0
24 разделочная 3 194.0
25 овощеварка 3 449.0
26 чайная 3 172.0
27 столовый 2 193.5
28 соковарка 2 1686.5
29 кисточка 2 59.0
30 термостакан 2 295.5
31 толкушка 2 651.5
32 сахарница 2 172.0
33 рыбочистка 2 97.0
34 сервировочная 2 28.0
35 блюдце 2 56.5
36 крышка 2 82.0
37 блюдо 2 262.0
38 кухонное 2 73.5
39 лопатка 2 55.5
40 мусорный 2 4030.5
41 бидон 2 138.0
42 миксер 2 801.5
43 просеиватель 2 202.0
44 венчик 1 67.0
45 тортница 1 824.0
46 бульонница 1 164.0
47 мантоварка 1 1349.0
48 сотейник 1 1162.0
49 скалка 1 1312.0
50 мантоварка-пароварка 1 2219.0
51 котел 1 2924.0
52 половник 1 83.0
53 отделитель 1 1649.0
54 овощечистка 1 749.0
55 модульная 1 825.0
56 электроштопор 1 1012.0

**Наблюдение:**

Распределить категорию "Все для кухни" можно следующим образом:

Основной:

  • Товары для готовки
  • Чайник
  • Соковарка
  • Мантоварка

Дополнительный:

  • наборы посуды (тарелки, кружки)
  • Терки
  • Столовые приборы
  • Сахарница
  • Овощечистка
  • Скалки

4. Проанализируем категорию "Товары для хранения вещей"

In [63]:
# Создадим таблицу
storage_tab = data.query('categories_product == "Товары для хранения вещей"')

# Сгруппируем данные по наименованию товара и месяцу
tab_4 = storage_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
        .sort_values(by='order_id',ascending=False)\
        .reset_index()
        
tab_4
Out[63]:
first_name order_id price
0 вешалка 36 142.0
1 полки 21 2249.0
2 сумка 13 374.0
3 подставка 6 366.5
4 вешалка-плечики 5 45.0
5 плечики 5 22.0
6 вешалки 4 164.0
7 комод 4 1349.0
8 этажерка 4 1027.0
9 вешалка-стойка 3 1837.0
10 стеллаж 3 1087.0
11 полка 2 1799.0
12 обувница-3 1 1912.0
13 складной 1 1424.0

**Наблюдение:**

Распределить категорию "Товары для хранения вещей" можно следующим образом:

Основной:

  • Стелаж
  • Этажерка
  • Обувница
  • Комод
  • Вешалки-стойки

Дополнительный:

  • плечики
  • сумки
  • полка

5. Проанализируем категорию "Хозтовары и инвентарь"

In [64]:
# Создадим таблицу
household_tab = data.query('categories_product == "Хозтовары и инвентарь"')

# Сгруппируем данные по наименованию товара и месяцу
tab_5 = household_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
        .sort_values(by='order_id',ascending=False)\
        .reset_index()
        
tab_5
Out[64]:
first_name order_id price
0 сумка-тележка 233 1649.0
1 муляж 166 59.0
2 тележка 96 734.0
3 таз 68 239.0
4 стремянка 28 1949.0
5 щетка 15 487.0
6 швабра 13 899.0
7 салфетка 12 196.5
8 новогоднее 12 1049.0
9 ведро 11 149.0
10 лестница-стремянка 11 1574.0
11 перчатки 9 224.0
12 щетка-сметка 8 61.5
13 окномойка 8 412.0
14 крючок 7 38.0
15 корыто 7 749.0
16 подарочный 6 194.0
17 камнеломка 5 97.0
18 стяжка 5 20.0
19 стремянки 5 3712.0
20 насадка 5 262.0
21 жестяная 5 104.0
22 шнур 5 37.0
23 петля 5 44.0
24 коробка 4 629.0
25 зубная 4 164.0
26 веник 4 339.5
27 термометр 3 44.0
28 ткань 3 285.0
29 урна 3 6899.0
30 совок 3 592.0
31 ящик 3 224.0
32 дозатор 3 524.0
33 крепеж 3 19.0
34 паста 3 899.0
35 мыло 3 78.0
36 вантуз 2 100.5
37 урна-пепельница 2 3760.5
38 измерительный 2 374.0
39 тряпкодержатель 2 119.0
40 пылесос 2 2121.5
41 шпагат 2 48.0
42 мешок 2 63.0
43 бензин 2 67.0
44 стремянка-табурет 2 2699.0
45 бельевые 2 66.5
46 ваза 1 488.0
47 шило 1 44.0
48 щетка-утюжок 1 44.0
49 щётка 1 1124.0
50 почтовый 1 277.0
51 кондиционер 1 262.0
52 фал 1 2099.0
53 корзинка 1 1049.0
54 лестница 1 974.0
55 стяжки 1 637.0
56 многофункциональный 1 637.0
57 петля-стрела 1 52.0
58 автоматическая 1 7229.0

**Наблюдение:**

Распределить категорию "Хозтовары и инвентарь" можно следующим образом:

Основной:

  • Лестницы и стремянки
  • Сумки-тележки
  • Урны
  • Пылесос

Дополнительный:

  • тазы, ведра
  • мешки
  • корзинка, щетка,

6. Проанализируем категорию "Все для спальни"

In [65]:
# Создадим таблицу
bedroom_tab = data.query('categories_product == "Все для спальни"')
# Сгруппируем данные по наименованию товара и месяцу
tab_6 = bedroom_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
        .sort_values(by='order_id',ascending=False)\
        .reset_index()
        
tab_6
Out[65]:
first_name order_id price
0 гладильная 118 1611.5
1 коврик 104 487.5
2 чехол 88 299.0
3 скатерть 31 974.0
4 подкладка 13 127.0
5 подрукавник 11 224.0
6 ковер 6 1012.0
7 кофр 6 531.5
8 рукав 5 225.0
9 покрывало 4 3261.5
10 плед 4 712.0
11 покрытие 4 1199.5
12 одеяло 3 1725.0
13 простыня 3 899.0
14 утюг 2 1060.5
15 доска 2 1724.0
16 подушка 2 239.5
17 ковёр 2 749.0
18 простынь 2 1852.0
19 светильник 1 1199.0
20 наматрасник 1 3074.0
21 наматрацник 1 1183.0
22 двуспальное 1 2024.0
23 пододеяльник 1 899.0

**Наблюдение:**

Распределить категорию "Все для спальни" можно следующим образом:

Основной:

  • Гладильная
  • Утюг
  • Матрасы/одеяла/подушки
  • ковер

Дополнительный:

  • постельное белье
  • чехлы, подкладки, подрукавники, рукава для гладильных досок
  • светильник

Определим сезонность по категориям¶

К сезонному товару можно определить только категорию "Все для цветов". Проанализируем ее.

In [84]:
# Сделаем временной срез с апреля по август и группировку по наименованию товара с подчетом заказов

flowers_seas = flowers_tab.query('month == ("2019-04-01","2019-08-01")')\
                          .groupby('first_name').agg({'order_id' :'count'})\
                          .reset_index().sort_values(by='order_id', ascending = False)[:10]
flowers_seas
Out[84]:
first_name order_id
36 пеларгония 142
39 рассада 109
46 томата 90
37 петуния 47
23 калибрахоа 27
4 бакопа 15
20 искусственный 15
34 однолетнее 13
50 цветок 8
40 роза 7
In [67]:
# Сделаем срез по рассаде и пеларгонии, петунии
rassada = flowers_tab.query('first_name == "рассада"').groupby('month').agg({'order_id' : 'count'}).reset_index()
pel = flowers_tab.query('first_name == "пеларгония"').groupby('month').agg({'order_id' : 'count'}).reset_index()
tomat = flowers_tab.query('first_name == "томата"').groupby('month').agg({'order_id' : 'count'}).reset_index()
petun = flowers_tab.query('first_name == "петуния"').groupby('month').agg({'order_id' : 'count'}).reset_index()
# Построим график
ax =rassada.plot(kind='bar', y = 'order_id', figsize = (15, 5), label='рассада', color='#008080',)
pel.plot(kind='bar', y = 'order_id', figsize = (15, 5), ax=ax, alpha=0.5, color='#da70d6', label='пеларгония')
tomat.plot(kind='bar', y = 'order_id', figsize = (15, 5), ax=ax, alpha=0.5, color='#cf0', label='томата')
petun.plot(kind='bar', y = 'order_id', figsize = (15, 5), ax=ax, alpha=0.5, color='#000080', label='петуния')
plt.xlabel('Месяц')
plt.ylabel('Количество заказов')
plt.title('График количества заказов растений')
ax.legend()
plt.show()

**Наблюдение:**

К сезонному товару можно отнести

  • рассаду
  • петунью
  • томата

Проверка гипотез¶

Для проверки двух гипотез выберем метод scipy.stats.ttest_ind( гипотеза о равенстве средних двух совокупностей)

Три аспекта, которые должны быть соблюдены:

  • генеральная совокупность не должна зависеть друг от друга;
  • выборочная средняя должна быть нормально распределена;
  • дисперсии рассматриваемых генеральных совокупностей должны быть равны

Проверка гипотезы №1 : "Среднии ежемесячные доходы по категориям "Все для цветов" и "Все для ванной" одинаковый"¶

Перед проверкой гипотезы найдем средний доходза месяц

In [68]:
# Сгруппируем данные по месяцу и категории
data_hypoth = data.groupby(['month','categories_product'])['revenue'].agg(['sum']).reset_index()
data_hypoth
Out[68]:
month categories_product sum
0 2018-10-01 Все для ванной 73656.0
1 2018-10-01 Все для кухни 34313.0
2 2018-10-01 Все для спальни 66237.0
3 2018-10-01 Все для цветов 30308.0
4 2018-10-01 Другое 16156.0
... ... ... ...
86 2019-10-01 Все для спальни 23456.0
87 2019-10-01 Все для цветов 21243.0
88 2019-10-01 Другое 5479.0
89 2019-10-01 Товары для хранения вещей 16225.0
90 2019-10-01 Хозтовары и инвентарь 90946.0

91 rows × 3 columns

In [69]:
# Сделаем срез по категории "Все для цветов" и "Все для ванной" и найдем среднию

data_hypoth_flowers = data_hypoth.query('categories_product == "Все для цветов"')['sum']
print('Средний доход в месяц по категории "Все для цветов"', round(data_hypoth_flowers.mean(),1))

data_hypoth_bath = data_hypoth.query('categories_product == "Все для ванной"')['sum']
print('Средний доход в месяц по категории "Все для ванной"', round(data_hypoth_bath.mean(), 1))
Средний доход в месяц по категории "Все для цветов" 38526.1
Средний доход в месяц по категории "Все для ванной" 39726.2

После определения среднего дохода приступим к формулировке гипотез

Формулировка гипотез:

  • Нулевая гипотеза(H_0): data_hypoth_flowers = data_hypoth_bath - Средние доходы категорий "Все для цветов" и "Все для ванной" одинаковые
  • Альтернативная гипотеза (H_a): data_hypoth_flowers ≠ data_hypoth_bath- Средние доходы категорий "Все для цветов" и "Все для ванной" разные

alpha = 0.05 - выберим данный уровень значимости (вероятный порог "необычности")

data_hypoth_flowers - средний доход за месяц категории "Все для цветов"\ data_hypoth_bath - средний доход за месяц категории "Все для ванной"

In [70]:
from scipy import stats as st

results = st.ttest_ind(
   data_hypoth_flowers, 
   data_hypoth_bath, equal_var = False) # results = вызов метода для проверки гипотезы
 
alpha = .05 # alpha уровнь значимости
 
print('p-значение:', results.pvalue) # вывод значения p-value на экран 

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу, средний ежемесячный доход категорий одинаковый")# условный оператор с выводом строки с ответом
p-значение: 0.8985080626295798
Не получилось отвергнуть нулевую гипотезу, средний ежемесячный доход категорий одинаковый

Найдем дисперссию двух генеральных совокупностей

In [71]:
a = np.var(data_hypoth_flowers)
b = np.var(data_hypoth_bath)
print(np.average(a))
print(np.average(b))
778458860.9940829
256028016.2840236

Построим гистограмму распределения среднего дохода категорий "Все для цветов" и "Все для ванной"

In [72]:
# Выведим таблицу в разрезе категорий, периода и дохода
month_category
Out[72]:
categories_product revenue
month NaT 2018-10-01 2018-11-01 2018-12-01 2019-01-01 2019-02-01 2019-03-01 2019-04-01 2019-05-01 2019-06-01 2019-07-01 2019-08-01 2019-09-01 2019-10-01
0 Все для ванной 73656.0 35282.0 47196.0 37088.0 59539.0 47284.0 22512.0 13439.0 31700.0 52218.0 22573.0 28983.0 44970.0
1 Все для кухни 34313.0 69663.0 32233.0 29623.0 41182.0 23787.0 7217.0 9007.0 4255.0 9889.0 12920.0 17514.0 5287.0
2 Все для спальни 66237.0 71451.0 93730.0 84394.0 46685.0 33245.0 43992.0 13083.0 52599.0 50311.0 17166.0 38089.0 23456.0
3 Все для цветов 30308.0 19777.0 11551.0 23833.0 38669.0 43285.0 95417.0 106029.0 37308.0 29282.0 24726.0 19411.0 21243.0
4 Другое 16156.0 6239.0 10942.0 8350.0 7358.0 8450.0 5903.0 2910.0 2762.0 2278.0 3397.0 7895.0 5479.0
5 Товары для хранения вещей 6479.0 10889.0 7635.0 10529.0 11882.0 14318.0 10773.0 7723.0 56205.0 6339.0 15483.0 17523.0 16225.0
6 Хозтовары и инвентарь 123225.0 146471.0 138623.0 40300.0 99131.0 79168.0 130308.0 76623.0 48795.0 76044.0 84645.0 48507.0 90946.0
In [73]:
# Построим гостограмму по среднему ежемесячному доходу двух категорий
figure(figsize=(10, 5), dpi=100)
data.query('categories_product == ["Все для цветов","Все для ванной"] & revenue> 1000')\
    .groupby('categories_product')['revenue']\
    .plot(kind='hist', bins=30, alpha=0.5)
plt.legend(["Все для цветов","Все для ванной"])
plt.xlabel('Выручка')
plt.ylabel('Кол-во')
plt.show()

Наблюдение: Вероятность p-value высока и равна 91%, что говорит о том, что нулевая гипотеза верна и значима. Кроме этого, данные математического расчета также это подтвердили. На гистограмме видно, многочисленное совпадение оценки пользователей двух категорий.

Проверка гипотезы №2 : "Средний чек на одного покупателя по категориям "Все для цветов" и "Все для ванной" одинаковый"¶

Перед проверкой гипотезы найдем среднюю чек покупателя

In [74]:
# Сгруппируем данные по покупателю и категории
data_hypoth_user = data.groupby(['customer_id','categories_product'])['revenue'].agg(['sum']).reset_index()
data_hypoth_user
Out[74]:
customer_id categories_product sum
0 000d6849-084e-4d9f-ac03-37174eaf60c4 Все для цветов 555.0
1 001cee7f-0b29-4716-b202-0042213ab038 Все для ванной 442.0
2 00299f34-5385-4d13-9aea-c80b81658e1b Хозтовары и инвентарь 914.0
3 002d4d3a-4a59-406b-86ec-c3314357e498 Хозтовары и инвентарь 1649.0
4 003bbd39-0000-41ff-b7f9-2ddaec152037 Товары для хранения вещей 2324.0
... ... ... ...
2541 ff601403-b094-4b86-9ac6-264d725b9277 Хозтовары и инвентарь 1649.0
2542 ffaeab76-3a8d-49ee-860f-17273b2fc8a2 Хозтовары и инвентарь 397.0
2543 ffb5976a-7a4d-460b-95c4-5ffaba31cb24 Хозтовары и инвентарь 389.0
2544 ffb80538-3fda-4351-8ea9-9d2bec58bb07 Все для ванной 974.0
2545 ffe82299-3f5b-4214-87fe-3d36ecccfac3 Все для ванной 577.0

2546 rows × 3 columns

In [75]:
# Сделаем срез по категории "Все для цветов" и "Все для ванной" и найдем среднию 

data_hypoth_flowers_user = data_hypoth_user.query('categories_product == "Все для цветов"')['sum']
print('Средний чек покупателя в месяц по категории "Все для цветов"', round(data_hypoth_flowers_user.mean(),1))

data_hypoth_bath_user = data_hypoth_user.query('categories_product == "Все для ванной"')['sum']
print('Средний чек покупателя по категории "Все для ванной"', round(data_hypoth_bath_user.mean(), 1))
Средний чек покупателя в месяц по категории "Все для цветов" 742.0
Средний чек покупателя по категории "Все для ванной" 1284.7

После определения среднего чека приступим к формулировке гипотез

Формулировка гипотез:

  • Нулевая гипотеза(H_0): ddata_hypoth_flowers_user = data_hypoth_bath_user - Средний чек на покупателя категорий "Все для цветов" и "Все для ванной" одинаковые
  • Альтернативная гипотеза (H_a): data_hypoth_flowers_user ≠ data_hypoth_bath_user- Средний чек на покупателя категорий "Все для цветов" и "Все для ванной" разные

alpha = 0.05 - выберим данный уровень значимости (вероятный порог "необычности")

data_hypoth_flowers_user - Средний чек на покупателя категории "Все для цветов"\ data_hypoth_bath_user - Средний чек на покупателя категории "Все для ванной"

In [76]:
from scipy import stats as st

results = st.ttest_ind(
   data_hypoth_flowers_user, 
   data_hypoth_bath_user, equal_var = False) # results = вызов метода для проверки гипотезы
 
alpha = .05 # alpha уровнь значимости
 
print('p-значение:', results.pvalue) # вывод значения p-value на экран 

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу, средние чеки на одного покупателя разные")
else:
    print("Не получилось отвергнуть нулевую гипотезу, средний чек на одгого покупателя двух категорий одинаковый")# условный оператор с выводом строки с ответом
p-значение: 0.0001773284239909108
Отвергаем нулевую гипотезу, средние чеки на одного покупателя разные

Найдем дисперссию двух генеральных совокупностей

In [77]:
a = np.var(data_hypoth_flowers_user)
b = np.var(data_hypoth_bath_user)
print(np.average(a))
print(np.average(b))
1257982.8160307251
7522341.502388555

Построим гистограмму распределения среднего чека на одного покупателя категорий "Все для цветов" и "Все для ванной"

In [78]:
figure(figsize=(10, 5), dpi=100)
data.query('categories_product == ["Все для цветов","Все для ванной"] & revenue> 2000').groupby('categories_product')['revenue']\
                                                             .plot(kind='hist', bins=30, alpha=0.5)
plt.legend(["Все для цветов","Все для ванной"])
plt.xlabel('Выручка')
plt.ylabel('Кол-во')
plt.show()

**Наблюдение:** Получив крайне маленькое значение p-value, мы отвергли Нулевую гипотезу. Таким образом, у нас практически нет вероятности получить одинаково средние чеки на одного покупателя. Перепроверка математическим путем и визуализация гистограммы нам это тоже показала.

Общий вывод и рекомендации¶

ВЫВОД:

В исходном датасете 6737 строк и 6 столбцов. После чистки данных и добавление столбцов получилось 4784 строк и 13 столбцов. 1.После того, как изучили датасеты, выявили задвоения данных: БЫЛО * Количество строк: 6737 * Количество уникальных покупателей: 2451 * Количество уникальных заказов: 2784 * Количество проданного товара: 16853 * Общая выручка: 4851280.0 * Средний чек заказа: 1742.5 СТАЛО * Количесво строк таблицы: 4784, данные после обработки уменьшилось на 28.99% * Кол-во уникальных покупателей: 2393, данные после обработки уменьшилось на 2.37% * Количество уникальных заказов:2754, данные после обработки уменьшилось на 1.08% * Количество проданного товара:12437, данные после обработки уменьшилось на 26.20% * Общая выручка:3411515.0, данные после обработки уменьшилось на 29.68% * Средний чек заказа:1238.7, данные после обработки уменьшилось на 28.91% Рекомендация: Стоит проверить выгрузку данных, перед составлением отчета, чтобы оптимизировать время. 2. Категории товаров\ После распределения товаров на группы, видно что более 78% заказов с одним товаром находятся в категориях "Все для цветов" и "Все для ванной". Рекомендация: Рекомендуем подробней изучить эти категории на предмет ассортимента. Возможно стоит предлагать дополнительный товар к этим группам для увеличения корзины. 3. Динамика продаж Четвертый квартал 2018 года был самый прибыльный из всех остальных кварталов 2019 года. В этом квартале были дорогие покупки в категориях "Все для кухни", "Все для ванной", "Все для спальни". Стоит детально изучить проданные товары на предмет мотивации у покупателей и понять, почему спрос упал. Рекомендация: Стоит увеличить ассортимент основгого товара в категориях "Все для кухни", например закупить современные и дизайнерские столы, стулья, технику. В категории "Все для ванной" дополнить ассортимент раковинами, зеркалами, ваннами, тумбами. Если говорить про дополнительный товар, то в категорию "Все для цветов" стоит добавить удобрения, свертства для роста и ухода цветов.

Презентация для заказчика¶

Подготовим презентацию исследования для заказчика.

Презентация: ссылка на облачное хранилище с презентацией

Дашборт¶

Просмотр дашборта: ссылка

In [ ]: